<?php

namespace Biz\module\Classes\redPacket;

use Biz\model\redPacket\RedPackets as mRedPackets;
use Biz\model\redPacket\RedPacketsLog as mRedPacketsLog;
use Biz\tools\OrderNo;
use Doraemon\tools\Log;
use Medoo\Medoo;
use Biz\model\trade\Order as mOrder;
use Biz\model\redPacket\RedPacketsAccount as mRedPacketsAccount;

class RedPacket
{
    /**
     * 红包收入
     * @param $uid
     * @param $configId
     * @param $orderNo
     * @param $amount
     * @param bool $adminSend 管理员发放
     * @param string $sendReason 发放原因
     * @return bool
     * @throws \Exception
     */
    public static function award($uid, $configId, $orderNo, $amount, $adminSend = false, $sendReason = '')
    {
        if (!$adminSend) {
            $isParent = OrderNo::isParentNo($orderNo);
            if ($isParent) {
                $orderNos = mOrder::select(['order_no'], ['order_parent_no' => $orderNo]);
                $orderNos = array_column($orderNos, 'order_no');
            } else {
                $orderNos[] = OrderNo::getParentNo($orderNo);
            }
            array_push($orderNos, $orderNo);
            if (mRedPacketsLog::hasIncome($uid, $orderNos)) {
                Log::warning('发放红包失败。你已经领过了。#uid:' . $uid . '#config_id:' . $configId . '#order_no:' . $orderNo . '#order_nos:' . json_encode($orderNos, JSON_UNESCAPED_UNICODE) . '#packet_amount:' . $amount);
                throw new \Exception('你已经领过了');
            }
        } else {
            $configId = $configId ? $configId : 0;
            $orderNo = $orderNo ? $orderNo : '';
        }
        $hasAccount = mRedPacketsAccount::hasAccount($uid);
        $result = mRedPackets::action(function () use ($uid, $configId, $orderNo, $amount, $hasAccount, $adminSend, $sendReason) {
            $result = false;
            $packetId = mRedPackets::award($uid, $configId, $amount);
            if ($packetId) {
                if ($hasAccount) {
                    $accountData = [
                        'packet_amount[+]' => $amount,
                        'current_amount[+]' => $amount,
                    ];
                    $result = mRedPacketsAccount::updateData($accountData, ['uid' => $uid]);
                } else {
                    $result = mRedPacketsAccount::addData(['uid' => $uid, 'packet_amount' => $amount, 'current_amount' => $amount]);
                }
                if ($result) {
                    $recordSource = [
                        'config_id' => $configId,
                        'packet_id' => $packetId,
                        'packet_amount' => $amount
                    ];
                    $logData = [
                        'uid' => $uid,
                        'type' => $adminSend ? mRedPacketsLog::TYPE_ADMIN_SEND : mRedPacketsLog::TYPE_INCOME,
                        'order_no' => $orderNo,
                        'packet_amount' => $amount,
                        'record_source' => json_encode($recordSource, JSON_UNESCAPED_UNICODE),
                        'remark' => $adminSend ? $sendReason : $orderNo
                    ];
                    $result = mRedPacketsLog::addData($logData);
                }
            }
            return $result;
        });
        if (!$result) {
            Log::warning('发放红包失败，数据操作错误。#uid:' . $uid . '#config_id:' . $configId . '#order_no:' . $orderNo . '#packet_amount:' . $amount);
        }
        return $result;
    }

    /**
     * 红包支出
     * @param $uid
     * @param $orderNo
     * @param $amount
     * @return mixed
     * @throws \Exception
     */
    public static function debuct($uid, $orderNo, $amount)
    {
        try {
            if (mRedPacketsLog::hasUse($uid, $orderNo)) {
                Log::warning('使用红包失败。你已经使用了红包。#uid:' . $uid . '#order_no:' . $orderNo . '#use_amount:' . $amount);
                throw new \Exception('你已经使用了红包');
            }
            $result = mRedPackets::action(function () use ($uid, $orderNo, $amount) {
                $result = false;
                $list = mRedPackets::select_master('*', Medoo::raw(' WHERE uid=:uid AND current_amount>0 ORDER BY id ASC for update', [':uid' => $uid]));
                if ($list) {
                    self::setCurrentAmount($list, 0, $amount);
                    $recordSource = [];
                    foreach ($list as $row) {
                        $id = $row['id'];
                        if (isset($row['is_change']) && $row['is_change']) {
                            $recordSource[$id] = [
                                'packet_id' => $id,
                                'config_id' => $row['config_id'],
                                'pay_amount' => $row['pay_amount']
                            ];
                            $res = mRedPackets::updateData(['current_amount' => $row['current_amount'], 'deduct_amount' => $row['deduct_amount']], ['id' => $id], $uid);
                            if (!$res) {
                                throw new \Exception('更新可用积分失败');
                            }
                        }
                    }
                    $result = mRedPacketsAccount::updateData(['current_amount[-]' => $amount], ['uid' => $uid]);
                    if ($result) {
                        $logData = [
                            'uid' => $uid,
                            'type' => mRedPacketsLog::TYPE_PAY,
                            'order_no' => $orderNo,
                            'packet_amount' => $amount,
                            'record_source' => json_encode($recordSource, JSON_UNESCAPED_UNICODE),
                        ];
                        $result = mRedPacketsLog::addData($logData);
                    }
                } else {
                    $msg = '用户红宝余额不足';
                    Log::warning($msg . '#uid:' . $uid . '#order_no:' . $orderNo);
                    throw new \Exception($msg);
                }
                return $result;
            });
            if (!$result) {
                Log::warning('使用红包失败#uid:' . $uid . '#order_no:' . $orderNo . '#amount:' . $amount);
            }
            return $result;
        } catch (\Exception $e) {
            Log::warning('使用红包失败#msg:' . $e->getMessage() . '#line:' . $e->getLine() . '#file:' . $e->getFile() . '#data:' . json_encode(['uid' => $uid, 'amount' => $amount, 'order_no' => $orderNo], JSON_UNESCAPED_UNICODE));
            return false;
        }
    }

    private static function setCurrentAmount(&$list, $key, $amount)
    {
        $info = $list[$key];
        if ($info['current_amount']) {
            $currentAmount = bcsub($info['current_amount'], $amount, 2);
            if ($currentAmount < 0) {
                $list[$key]['current_amount'] = 0;
                $list[$key]['deduct_amount'] = bcadd($list[$key]['deduct_amount'], $info['current_amount'], 2);
                $list[$key]['is_change'] = true;
                $list[$key]['pay_amount'] = $info['current_amount'];
                if (isset($list[$key + 1])) {
                    self::setCurrentAmount($list, $key + 1, abs($currentAmount));
                }
            } else {
                $list[$key]['current_amount'] = $currentAmount;
                $list[$key]['deduct_amount'] = bcadd($list[$key]['deduct_amount'], $amount, 2);
                $list[$key]['is_change'] = true;
                $list[$key]['pay_amount'] = $amount;
            }
        } else {
            if (isset($list[$key + 1])) {
                self::setCurrentAmount($list, $key + 1, $amount);
            }
        }
    }

    /**
     * 红包退还
     * @param $uid
     * @param $orderNo
     * @param $amount
     * @return bool
     */
    public static function refund($uid, $orderNo, $amount)
    {
        $result = false;
        $logInfo = mRedPacketsLog::getPayLog($uid, $orderNo);
        if ($logInfo) {
            if (bccomp($logInfo['packet_amount'], $amount, 2) != 0) {
                Log::warning('退还红包失败，申请退的红包和订单使用的红包金额不一致#uid:' . $uid . '#order_no:' . $orderNo . '#packet_amount:' . $logInfo['packet_amount'] . '#refund_amount:' . $amount);
            } else {
                $recordSource = json_decode($logInfo['record_source'], true);
                if ($recordSource) {
                    $result = mRedPackets::action(function () use ($amount, $uid, $orderNo, $recordSource) {
                        $result = false;
                        $addResource = [];
                        foreach ($recordSource as $packetId => $row) {
                            $refundAmount = $row['pay_amount'];
                            $data = [
                                'deduct_amount[-]' => $refundAmount,
                                'current_amount[+]' => $refundAmount,
                            ];
                            $addResource[$packetId] = [
                                'packet_id' => $packetId,
                                'config_id' => $row['config_id'],
                                'refund_amount' => $refundAmount
                            ];
                            $result = mRedPackets::updateData($data, ['id' => $packetId]);
                        }
                        if ($result) {
                            $result = mRedPacketsAccount::updateData(['current_amount[+]' => $amount], ['uid' => $uid]);
                            if ($result) {
                                $logData = [
                                    'uid' => $uid,
                                    'type' => mRedPacketsLog::TYPE_REFUND,
                                    'order_no' => $orderNo,
                                    'packet_amount' => $amount,
                                    'record_source' => json_encode($addResource, JSON_UNESCAPED_UNICODE),
                                ];
                                $result = mRedPacketsLog::addData($logData);
                            }
                        }
                        return $result;
                    });
                    if (!$result) {
                        Log::warning('退还红包失败#uid:' . $uid . '#order_no:' . $orderNo);
                    }
                }
            }
        } else {
            Log::warning('退还红包失败，该订单没有使用红包#uid:' . $uid . '#order_no:' . $orderNo);
        }
        return $result;
    }

}